package cn.nr19.jian.factory

import cn.nr19.jian.E2Factory
import cn.nr19.jian.J2Utils
import cn.nr19.jian.Jian
import cn.nr19.jian.exception.JSParserException
import cn.nr19.jian.exception.ParserException
import cn.nr19.jian.`object`.JianLei2
import cn.nr19.jian.`object`.JianLeiApi
import org.json.JSONArray
import org.json.JSONObject
import org.mozilla.javascript.*
import java.io.Serializable
import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap

/**
 * JS解析封装
 * 继承 Serializable 只是单纯懒得去写混淆规则  这个不能被混淆
 * @param JsStr 目标源码
 * @param varObj 变量管理器
 * @param 类
 */

/**
 * @param dataCode 源
 * 继承 Serializable 仅为了让写不可混淆代码 无其他作用
 */
class JsFactory(val CODE : String, val  lei : JianLeiApi, val cryptojs : String) : Serializable{

    private val clazz: Class<*>

    // js方法语句
    private var allFunctions = ""

    /**
     * 执行JS 错误不处理
     * @param js  js执行代码 eg: "var v1 = getValue('Ta');setValue(‘key’，v1);"
     */
    fun p(js: String) : String {

        try {
            return run2(js)
        }
        catch (e : ParserException){
            throw e
        }
        catch (e : Exception){
            J2Utils.log("a----------------------------------------------")
            e.printStackTrace()
            J2Utils.log("b----------------------------------------------")

            throw JSParserException("$e :\n\n $js")
        }
    }

    /**
     * E3BaseParser 中执行 直接报错E3
     */
    fun run2(js: String) : String {
        val runJSStr = (allFunctions + "\n\n $js").trimIndent() //运行js = allFunctions + js
        try {

            val rhino = Context.enter()
            rhino.optimizationLevel = -1
            rhino.languageVersion = 200

            val scope: Scriptable = rhino.initStandardObjects()
            ScriptableObject.putProperty(scope, "javaContext", Context.javaToJS(this, scope)) // 配置属性 javaContext:当前类JSEngine的上下文
            ScriptableObject.putProperty(scope, "javaLoader", Context.javaToJS(clazz.classLoader, scope)) // 配置属性 javaLoader:当前类的JSEngine的类加载器
            var obj  : Any? = rhino.evaluateString(scope, runJSStr, clazz.simpleName, 1, null)
            if (obj is NativeJavaObject) obj = obj.unwrap()
            return obj.toString()
        }
        catch (e : Exception){
            e.printStackTrace()
            throw JSParserException("$e :\n\n $js")
        }
        finally {
            Context.exit()
        }
    }


    init {
        clazz = JsFactory::class.java
        allFunctions = String.format(getAllFunctions(), clazz.name) //生成js语法
        allFunctions += "\n\n" + cryptojs

        /*CacheMemoryStaticUtils.get<String?>("jsutils_allfunction").let {
            if (it == null) {
                allFunctions = String.format(getAllFunctions(), clazz.name) //生成js语法
                allFunctions += "\n\n" + UFile.getAssets2String(App.ctx(),"js/cryptojs.js")
                CacheMemoryStaticUtils.put("jsutils_allfunction",allFunctions)
            }else{
                allFunctions = it
            }
        }*/
    }

    /**
     * 通过注解自动生成js方法语句
     */
    private fun getAllFunctions(): String {
        var funcStr = " var ScriptAPI = java.lang.Class.forName(\"%s\", true, javaLoader);\n"
        val cls: Class<*> = this.javaClass
        for (method in cls.declaredMethods) {
            val an = method.getAnnotation(JSAnnotation::class.java) ?: continue
            val functionName = method.name
            var paramsTypeString = "" //获取function的参数类型
            var paramsNameString = "" //获取function的参数名称
            var paramsNameInvokeString = ""
            val parmTypeArray = method.parameterTypes
            if (parmTypeArray.size > 0) {
                val parmStrArray = arrayOfNulls<String>(parmTypeArray.size)
                val parmNameArray = arrayOfNulls<String>(parmTypeArray.size)
                for (i in parmTypeArray.indices) {
                    parmStrArray[i] = parmTypeArray[i].name
                    parmNameArray[i] = "param$i"
                }

                paramsTypeString = String.format(",[%s]", parmStrArray.joinToString(","))
                paramsNameString = parmNameArray.joinToString(",")
                paramsNameInvokeString = ",$paramsNameString"
            }
            val returnType = method.returnType
            val returnStr = if (returnType.simpleName == "void") "" else "return" //是否有返回值
            val methodStr = String.format(" var method_%s = ScriptAPI.getMethod(\"%s\"%s);\n",
                functionName, functionName, paramsTypeString
            )
            var functionStr = ""
            when(an.returnType){
                // 字符串
                1 -> {
                    functionStr = String.format(""" function %s(%s){var retStr = method_%s.invoke(javaContext%s);return retStr + '';}""".trimMargin(),
                            functionName, paramsNameString, functionName, paramsNameInvokeString
                    )
                }

                // 对象
                2 -> {
                    functionStr = String.format(""" function %s(%s){var retStr = method_%s.invoke(javaContext%s);return JSON.parse(retStr);}""".trimMargin(),
                            functionName, paramsNameString, functionName, paramsNameInvokeString
                    )
                }

                // 布尔型
                3 -> {
                    functionStr = String.format(""" function %s(%s){var retStr = method_%s.invoke(javaContext%s);return retStr + '' == 'true';}""".trimMargin(),
                            functionName, paramsNameString, functionName, paramsNameInvokeString
                    )
                }

                // 通用类型
                4 -> {
                    functionStr = String.format(""" function %s(%s){return method_%s.invoke(javaContext%s);}""".trimMargin(),
                        functionName, paramsNameString, functionName, paramsNameInvokeString
                    )
                }

                // 0 无返回
                else ->{
                    functionStr = String.format(""" function %s(%s){%s method_%s.invoke(javaContext%s);}""".trimMargin(),
                            functionName, paramsNameString, returnStr, functionName, paramsNameInvokeString
                    )
                }
            }

            funcStr = funcStr + methodStr + functionStr
            // funcStr += "\n function request(obj){ return m_request(JSON.stringify(obj))}"
        }

        funcStr += " \n function request(obj){if(typeof(obj) == \"string\") {return request({'url':obj})}else{return m_request(JSON.stringify(obj))}}"
        funcStr += "\n function 打开(url){return open(url)};"
        funcStr += "\n function evalE3(str){return e3(str)};"
        return funcStr
    }

    /**
     * 注解 确定函数返回内容
     */
    @Suppress("SupportAnnotationUsage")
    @Target(value = [ElementType.METHOD])
    @Retention(value = RetentionPolicy.RUNTIME)
    annotation class JSAnnotation(
            /**
             * 函数返回类型
             * 0 无返回类型
             * 1 字符串
             * 2 数组对象
             * 3 布尔型
             * 4 字节集
             */
            val returnType: Int = 0
    )

    /************** 所有 函数 **************************/


    /**
     * 弹窗
     */
    @JSAnnotation
    fun alert(value : String){
        lei.pJianSystemFun("信息框",value)
    }

    /**
     * 获取变量
     */
    @JSAnnotation(1)
    fun getVar(sign : String): String {
        return lei.getVar(sign)?.toString() ?:"" // 不报错
    }

    @JSAnnotation(1)
    fun getCode() : String = CODE

    /*@JSAnnotation(1)
    fun getValue(keyStr: String): String? {
        return "获取到值了"
    }

    @JSAnnotation
    fun setValue(keyStr: Any, o: Any) {
        App.log("JSEngine output - setValue : $keyStr ------> $o")
    }*/

    @JSAnnotation
    fun putVar(sign : String,value : String){
        lei.createVar(sign,value)
    }


    /* 解析E2 */
    @JSAnnotation(1)
    fun e2(code : String,rule : String) : String{
        return E2Factory.text(code, rule,lei)
    }
    @JSAnnotation(1)
    fun e2Rex(code : String,rule : String) : String{
        return e2(code, rule)
    }

    /**
     * 引入CryptoJs
     */
    /*@JSAnnotation(1)
    fun getCryptoJS(): String? {
        return try {
            UFile.getAssets2String(App.ctx,"js/cryptojs.js")
        } catch (e: Exception) {
            ""
        }
    }*/

    @JSAnnotation(1)
    fun E2(code: String,rule: String):String = e2Rex(code, rule)

    @JSAnnotation(2)
    fun e2Arr(code : String,rule : String) : String{
        E2Factory.list(code,rule,lei)?.let {
            val json = JSONArray()
            it.forEach {
                json.put(it)
            }
            return json.toString()
        }

        return ""
    }

    @JSAnnotation(1)
    fun getWebStr(url : String) : String{
        return Jian.Str(lei.pJianSystemFun("读源码",url))
    }

    @JSAnnotation(1)
    fun getHttp(obj: String): String {
        return Jian.Str(lei.pJianSystemFun("读源码2JSON2",obj))
    }

    @JSAnnotation(1)
    fun get1(url: String) : String{
        return Jian.Str(lei.pJianSystemFun("读源码",url))
    }

    @JSAnnotation(1)
    fun get0(url : String) : String{
        // jsoup方式读
        return Jian.Str(lei.pJianSystemFun("读源码2JSOUP",url))
    }

    /**
     * 加载网络，返回对象
     */
    @JSAnnotation(2)
    fun JZ(obj: String) : String {
        return m_request(obj)
    }


    /**
     * 写入网络文件
     * @param saveName 保存名
     */
    @JSAnnotation(3)
    fun writeNetFile(saveName : String,obj : String) : Boolean{
        val value = m_request(obj)
        return Jian.True(lei.pJianSystemFun("写文件2",saveName,m_request(obj)))


    }

    @JSAnnotation(2)
    fun m_request(str : String) : String{
        return Jian.Str(lei.pJianSystemFun("读源码2JSON",str))
    }

    @JSAnnotation(2)
    fun 加载(obj: String) : String?= JZ(obj)

    /* 报错 */
    @JSAnnotation
    fun BC(msg : String){
        throw Exception("执行JS时发生错误 $msg")
    }

    @JSAnnotation
    fun 报错(msg: String) = BC(msg)

    /**
     * 网页提交类
     */



    @JSAnnotation
    fun writeCahce(key : String,content: String) : Boolean{


        return Jian.True(lei.pJianSystemFun("写缓存",key,content))
    }



    /*

@JSAnnotation(3)
    fun writeByteArray(fileName: String,content: ByteArray) : Boolean{
        if (e3Data == null) return false
        return QvUtils.writeFile(e3Data.getTaskData().sqlID,fileName,content)
    }

    @JSAnnotation
    fun writeCahceByteArray(key : String,content: String) : Boolean{
        if (e3Data == null) return false
        return QvUtils.writeCache(e3Data.getTaskData().sqlID,key,content)
    }*/

    /*

    @JSAnnotation(4)
    fun readCacheByteArray(key: String) : ByteArray{
        if (e3Data == null) return "".toByteArray()
        return QvUtils.readCache2(e3Data.getTaskData().sqlID,key)
    }*/

    @JSAnnotation(1)
    fun readCache(key: String) : String{
        lei.pJianSystemFun("读缓存",key).let {
            if (it is String) return it
            else return ""
        }
    }

    @JSAnnotation(3)
    fun downloadStr(fileName : String,content : String) : Boolean{
        return Jian.True(lei.pJianSystemFun("下载",fileName,content))
        // return QvUtils.downloadFile(fileName,content)
    }

    @JSAnnotation(3)
    fun writeStr(fileName : String,content : String) : Boolean{
        return Jian.True(lei.pJianSystemFun("写文件",fileName,content))
        //return QvUtils.writeFile(e3Data.getTaskData().qlid,fileName,content)
    }

    @JSAnnotation(1)
    fun readStr(fileName : String) : String{
        lei.pJianSystemFun("读文件",fileName).let {
            if (it is String) return it
            else return ""
        }
        // return QvUtils.readFileStr(e3Data.getTaskData().qlid,fileName)
    }


    @JSAnnotation(3)
    fun open(url : String) : Boolean{

        return Jian.True(lei.pJianSystemFun("打开",url))
    }

    @JSAnnotation(1)
    fun e3(str : String) : String{
        try {
            return Jian.p(str, JianLei2(lei)).toString()
        }catch (e : Exception){
            throw Exception(e)
        }
    }





}